/***************************************************************************************************
Copyright 2009 - 2010 Harshana Eranga Martin, Dishan Metihakwala, Rajeev Sampath, Chathura Randika

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 ****************************************************************************************************/
package org.epzilla.leader.service;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;

import org.epzilla.common.discovery.node.NodeDiscoveryManager;
import org.epzilla.leader.Epzilla;
import org.epzilla.leader.client.NodeClientManager;
import org.epzilla.leader.message.RmiMessageClient;
import org.epzilla.leader.util.Status;
import org.epzilla.leader.util.SystemConstants;

/**
 * This is the update service for the nodes.
 * 
 * @author Harshana Eranga Martin mailto: harshana05@gmail.com
 * 
 */
public class NodeUpdateService extends Thread implements IEpzillaService {
	// Private constructor prevents instantiation from other classes
	private NodeUpdateService() {
		this.setDaemon(true);
	}

	public void run() {
		while (true) {
			try {
				Thread.sleep(SystemConstants.UPDATE_SERVICE_RUNNING_TIME);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			executeService();
		}
	}

	/**
	 * SingletonHolder is loaded on the first execution of
	 * Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
	 * not before.
	 */
	private static class NodeUpdateServiceHolder {
		private static final NodeUpdateService INSTANCE = new NodeUpdateService();
	}

	public static NodeUpdateService getInstance() {
		return NodeUpdateServiceHolder.INSTANCE;
	}

	/**
	 * Execute Normal component update routine.
	 */
	public void executeService() {
		System.out.println("Starting general update routine.");

		HashSet<String> currentNodeList = new HashSet<String>(
				NodeClientManager.getNodeList());
		HashSet<String> respondedList = new HashSet<String>();
		try {
			currentNodeList.remove(InetAddress.getLocalHost().getHostAddress());
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

		for (Iterator<String> iterator = currentNodeList.iterator(); iterator
				.hasNext();) {
			final String string = (String) iterator.next();
			String response = RmiMessageClient.getStateFromRemote(string);
			if (response != null) {
				respondedList.add(string);
			}
		}

		if (NodeClientManager.getNodeList().size() == currentNodeList.size() + 1) {
			// No new updates to node list
			currentNodeList.removeAll(respondedList);
			for (Iterator<String> iterator = currentNodeList.iterator(); iterator
					.hasNext();) {
				String string = (String) iterator.next();
				NodeDiscoveryManager.getNodePublisher().removeSubscrition(
						string, "UNSUBSCRIBE_NODE_SERVICE");
				System.out.println("Removed a dead node from node list :"
						+ string + "Removed by "
						+ Thread.currentThread().getId());
			}

		}

		if (Epzilla.getStatus().equalsIgnoreCase(Status.LEADER.name()))
			executeLeaderTasks(respondedList);

		currentNodeList = null;
		respondedList = null;
		System.gc();
	}

	/**
	 * Tasks that are executed only by a cluster Leader
	 * 
	 * @param respondedList
	 *            - Set of nodes who responded to the normal tasks
	 */
	private void executeLeaderTasks(HashSet<String> respondedList) {
		System.out.println("Starting leader update routine.");

		HashSet<String> currentSubscribedNodeList = new HashSet<String>(
				NodeClientManager.getSubscribedNodeList());
		HashSet<String> currentDispatcherList = new HashSet<String>(
				NodeClientManager.getDispatcherList());
		HashSet<String> respondedDispatcherList = new HashSet<String>();

		if (NodeClientManager.getSubscribedNodeList().size() == currentSubscribedNodeList
				.size()) {
			// No new updated for subscribed list
			currentSubscribedNodeList.removeAll(respondedList);
			for (Iterator<String> iterator = currentSubscribedNodeList
					.iterator(); iterator.hasNext();) {
				String string = (String) iterator.next();
				NodeDiscoveryManager.getLeaderPublisher().removeSubscrition(
						string, "UNSUBSCRIBE_LEADER_SERVICE");
				System.out.println("Removed a dead node from subscribers :"
						+ string + "Removed by "
						+ Thread.currentThread().getId());

			}
		}

		for (Iterator<String> iterator = currentDispatcherList.iterator(); iterator
				.hasNext();) {
			final String string = (String) iterator.next();
			String response = RmiMessageClient.getStateFromRemote(string);
			if (response != null) {
				respondedDispatcherList.add(string);
			}
		}

		if (NodeClientManager.getDispatcherList().size() == currentDispatcherList
				.size()) {
			// No new dispatcher has arrived.
			currentDispatcherList.removeAll(respondedDispatcherList);
			for (Iterator<String> iterator = currentDispatcherList.iterator(); iterator
					.hasNext();) {
				String string = (String) iterator.next();
				NodeDiscoveryManager.getLeaderPublisher().removeDispatcherList(
						string);
				System.out
						.println("Removed a dispatcher from Dispatcher list :"
								+ string);

			}
		}

		currentSubscribedNodeList = null;
		currentDispatcherList = null;
		respondedDispatcherList = null;
		respondedList = null;
	}

}
